package org.dce.screencapturer.editors;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.dce.screencapturer.objects.LineAttributes;
import org.dce.screencapturer.objects.ShapeObject;

@SuppressWarnings("serial")
public class PaintEditor extends Editor {

	private JColorChooser chooser = new JColorChooser();
	private JCheckBox enablePaint = new JCheckBox("Paint border?");
	private JSpinner strokeWidth = new JSpinner();
	private StrokeSample sample = new StrokeSample();
	private JComboBox patterns = new JComboBox();
	
	public PaintEditor(ShapeObject editedObject){
		super(editedObject,"Edit paint");
		
		add(createPaintTab(),BorderLayout.CENTER);
	}

	private Component createPaintTab() {
		JPanel panel = new JPanel(new BorderLayout());
		panel.add(enablePaint,BorderLayout.NORTH);
		JTabbedPane tabs = new JTabbedPane();
		tabs.addTab("Color", chooser);
		tabs.addTab("Stroke", createStrokeTab());
		panel.add(tabs,BorderLayout.CENTER);
		return panel;
	}
	
	private Component createStrokeTab(){
		JPanel panel = new JPanel(new GridBagLayout());
		
		panel.add(new JLabel("Width"),new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
		strokeWidth.setPreferredSize(new Dimension(60,22));
		panel.add(strokeWidth,new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
		panel.add(new JLabel("Pattern"),new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
		patterns.setPreferredSize(new Dimension(180,22));
		patterns.setModel(createPatternsModel());
		patterns.setRenderer(new PatternRenderer());
		
		patterns.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				updateSample();
			}
		});
		
		panel.add(patterns,new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
		
		strokeWidth.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent e) {
				updateSample();
			}
		});
		
		sample.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Sample"));
		panel.add(sample,new GridBagConstraints(0, 2, 2, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0,0,0,0), 0, 0));
		
		return panel;
	}
	
	private void updateSample(){
		
		BasicStroke stroke = createStroke();
		
		sample.setStroke(stroke);
	}

	private BasicStroke createStroke() {

		Pattern pattern = (Pattern)patterns.getSelectedItem();
		
		float width = ((Number)strokeWidth.getValue()).floatValue();
		
		BasicStroke stroke = new BasicStroke(
				width, 
				BasicStroke.CAP_SQUARE, 
				BasicStroke.JOIN_ROUND, 
				0,
				pattern.dash,
				pattern.dashPhase);
		
		return stroke;
	}
	
	private ComboBoxModel createPatternsModel(){
		
		DefaultComboBoxModel model = new DefaultComboBoxModel();
		model.addElement(new Pattern(0));
		
		for (int i=1;i<=30;i++){
			model.addElement(new Pattern(1,i+1,i));
			model.addElement(new Pattern(1,i,i+1));
		}
		
		return model;
	}

	private class StrokeSample extends JComponent {
		private Stroke stroke = new BasicStroke(1);
		
		public void setStroke(Stroke stroke) {
			this.stroke = stroke;
			repaint();
		}

		protected void paintComponent(Graphics g){
			Graphics2D g2d = (Graphics2D)g;
			
			Stroke currentStroke = g2d.getStroke();
			
			g2d.setStroke(stroke);
			g2d.drawLine(10, getHeight()/2, getWidth()-20, getHeight()/2);
			
			g2d.setStroke(currentStroke);
		}
	}
	
	@Override
	protected void readFromObject() {
		LineAttributes lineAttributes = editedObject.getLineAttributes();
		boolean enabled = lineAttributes.isEnabled();
		enablePaint.setSelected(enabled);
		chooser.setColor(lineAttributes.getColor());
		
		BasicStroke stroke = lineAttributes.getStroke();
		
		strokeWidth.setValue(stroke.getLineWidth());
		patterns.setSelectedItem(new Pattern(stroke.getDashPhase(),stroke.getDashArray()));
	}

	@Override
	protected void writeToObject() {
		
		LineAttributes lineAttributes = (LineAttributes)editedObject.getLineAttributes().clone();
		lineAttributes.setEnabled(enablePaint.isSelected());
		lineAttributes.setColor(chooser.getColor());
		lineAttributes.setStroke(createStroke());
		editedObject.setLineAttributes(lineAttributes);
		
	}
	
	private class Pattern {
		private float dashPhase;
		private float[] dash;
		
		public Pattern(float dashPhase, float ... dash){
			this.dashPhase = dashPhase;
			this.dash = (dash == null || dash.length == 0) ? null : dash;
		}
		
		public int hashCode(){
			return (toString(dash)+dashPhase).hashCode();
		}
		
		public boolean equals(Object other){
			return other instanceof Pattern
				&& dashPhase == ((Pattern)other).dashPhase
				&& areEquals(dash, ((Pattern)other).dash);
		}
		
		private boolean areEquals(float[] array1,float[] array2){
			return toString(array1).equals(toString(array2));
		}
		private String toString(float[] array){
			StringBuilder builder = new StringBuilder();
			if (array != null){
				for (int i=0;i<array.length;i++){
					builder.append(array[i]).append(" ");
				}
			}
			return builder.toString();
		}
	}
	
	private class PatternRenderer extends DefaultListCellRenderer {
		
		private Pattern currentPattern;
		
		protected void paintComponent(Graphics g){
			
			Insets insets = getInsets();
			
			g.setColor(getBackground());
			
			g.fillRect(0,0,getWidth()-1,getHeight()-1);
			
			g.setColor(getForeground());
			
			BasicStroke stroke = new BasicStroke(
				1, 
				BasicStroke.CAP_SQUARE, 
				BasicStroke.JOIN_ROUND, 
				0,
				currentPattern.dash,
				currentPattern.dashPhase);
			
			Graphics2D g2d = (Graphics2D)g;
			
			Stroke current = g2d.getStroke();
			
			g2d.setStroke(stroke);
			
			g.drawLine(insets.left, getHeight()/2, getWidth()-insets.right-insets.left, getHeight()/2);
			
			g2d.setStroke(current);
		}

		@Override
		public Component getListCellRendererComponent(JList list, Object value,
				int index, boolean isSelected, boolean cellHasFocus) {
			
			PatternRenderer renderer = (PatternRenderer)super.getListCellRendererComponent(list, value, index, isSelected,
					cellHasFocus);
			
			renderer.currentPattern = (Pattern)value;
			
			return renderer;
		}
		
		
	}
}
